﻿using Red.Core.Network.PacketNetwork;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Red.CommUtils.Application
{
    public static class AppUtils
    {
        static public string engineBinPath;
        static public string enginePath;

        public static string GetAppName( string binaryPath )
        {
            int appNameStartingIndex = binaryPath.LastIndexOfAny( new char[] { '\\', '/' } );
            int appNameEndingIndex = binaryPath.LastIndexOf( ".exe" );
            appNameStartingIndex++;     // Skip first character
            return binaryPath.Substring( appNameStartingIndex, appNameEndingIndex - appNameStartingIndex );
        }

        public static bool FindExistingAppProcess( string appName, out Process process )
        {
            Process[] pname = Process.GetProcessesByName( appName );

            if ( pname.Length > 0 )
                process = pname[0];
            else
                process = null;

            return process != null;
        }

        public static Process LaunchNewProcess( string binaryPath, string commandLine )
        {
            try
            {
                return Process.Start( binaryPath, commandLine );
            }
            catch ( Win32Exception ex )
            {
                throw new AppLauncherException( "Exception occurred when trying to launch process: " + ex.Message );
            }
        }
    }

    public class AppRestoreLocalPC : IAppRestore
    {
        private string _binaryPath;
        private string _commandLine;
        private string _crashFileNameWithoutExtension;

        public string CrashFilePath
        {
            get { return _crashFileNameWithoutExtension + ".crashinfo"; }
        }

        public AppRestoreLocalPC( string binaryPath, string commandLine )
        {
            _binaryPath = binaryPath;
            _commandLine = commandLine + " -restore";

            DateTime now = DateTime.Now;
            string timestamp = "_" + now.Year + "_" + now.Month + "_" + now.Day + "_" + now.Hour + "_" + now.Minute + "_";
            _crashFileNameWithoutExtension = _binaryPath + timestamp + ".crashinfo";
        }

        public void Restore()
        {
            Process process;

            string appName = AppUtils.GetAppName( _binaryPath );

            if ( AppUtils.FindExistingAppProcess( appName, out process ) == true )
            {
                process.Kill();
                process.WaitForExit();
            }

            string commanLineWithCrashFileName = _commandLine + " -crashInfoFileName=" + _crashFileNameWithoutExtension;

            process = AppUtils.LaunchNewProcess( _binaryPath, commanLineWithCrashFileName );
        }
    }

    public class AppLauncherLocalPC : IAppLauncher
    {
        ChannelFactory _channelFactory;
        private string _binaryPath;
        private string _crashFileNameWithoutExtension;

        public string CrashFilePath
        {
            get { return _crashFileNameWithoutExtension + ".crashinfo"; }
        }

        public AppLauncherLocalPC( ChannelFactory channelFactory, string binaryPath )
        {
            _binaryPath = binaryPath;
            _channelFactory = channelFactory;

            AppUtils.engineBinPath = binaryPath + "/../";
            AppUtils.enginePath = binaryPath + "/../../../engine/";

            DateTime now = DateTime.Now;
            string timestamp = "_" + now.Year + "_" + now.Month + "_" + now.Day + "_" + now.Hour + "_" + now.Minute + "_";
            _crashFileNameWithoutExtension = _binaryPath + timestamp + ".crashinfo";
        }

        public async Task<IAppHandle> Launch( string[] arguments )
        {
            string commandLine = ConstructLaunchArguments( arguments );
            IAppRestore restore = new AppRestoreLocalPC( _binaryPath, commandLine );

            AppHandle handle = new AppHandle( "EngineLauncher", "Engine Launcher (Local PC)", CrashFilePath, _channelFactory, IPAddress.Loopback, restore );

            handle.InitializeEditorChannel(); // must be called before engine process is launched

            Process process;
            
            string appName = AppUtils.GetAppName( _binaryPath );
            string commanLineWithCrashFileName = commandLine + " -crashInfoFileName=" + _crashFileNameWithoutExtension;

            if ( AppUtils.FindExistingAppProcess( appName, out process ) == false )
                process = AppUtils.LaunchNewProcess( _binaryPath, commanLineWithCrashFileName );

            bool connected = await handle.ConnectToChannels();        // NOTE: must be called after new engine process is launched
            
            return handle;
        }

        private string ConstructLaunchArguments( string[] userArguments )
        {
            string result = "";

            foreach ( string arg in userArguments )
            {
                result += " " + arg;
            }

            return result;
        }
    }
}
